from econ_analysis.models import FishingImpactCache
"""
AnalysisCache.py
This file houses functionality related to Impact Analysis caching.
    Checking whether cache is available and up to date for a particular mpa on a given group (and port)
    
"""

'''
This function will replace any cached results with new results, or simply add results if none are there already
Called from MpaAnalysis.get_mpa_results 
'''    
def cache_analysis_results(results, group, mpa, port=None):
    #remove any rows that may already exist for this mpa, group
    if port is None:
        old_cache = FishingImpactCache.objects.filter(mpa__id=mpa.id, group=group)
    else:
        old_cache = FishingImpactCache.objects.filter(mpa__id=mpa.id, group=group, port=port)
    for single_row in old_cache:
        single_row.delete() 
    #cache results
    for result in results:
        cache = FishingImpactCache(mpa_id=mpa.id, group=group, port=result.port, species=result.species, perc_value=result.percOverallValue, perc_area=result.percOverallArea, wkt_hash=str(mpa.geometry_final.wkt.__hash__()))
        cache.save()
 
'''
Returns a boolean value based on whether an array for a given fishing group has been cached 
and that this cache is current (mpa geometries or allowed uses have not been modified since caching)
Called by views.impact_analysis
'''
def array_cache_is_available_and_current(array, group, single_port=None):
    mpas = array.mpa_set
    for mpa in mpas:
        if mpa.designation_id is not None:
            if not mpa_cache_is_available_and_current(mpa, group, single_port):
                return False
    return True
          
'''
Returns a boolean value based on whether an mpa (or an mpa with matching geometry) for a given fishing group has been cached 
and that this cache is current (mpa geometry or allowed uses have not been modified since caching)
Called by ProcessingTimes.estimate_time_for_mpa and views.impact_analysis
'''
def mpa_cache_is_available_and_current(mpa, group, single_port=None):
    if cache_is_available(mpa, group, single_port):
        mpa_cache = get_mpa_cache(mpa, group, single_port)
        if cache_is_complete_and_uptodate(mpa, mpa_cache, group, single_port):
            return True
    return False 
       
'''
Checks to see if cache exists for a given mpa on a given group
Returns True if any cache is present -- does not check whether cache is complete or current
Called by mpa_cache_is_available_and_current
'''       
def cache_is_available(mpa, group, port=None):
    from utilities import ensure_proper_name
    group = ensure_proper_name(group)
    from nc_mlpa.econ_analysis.models import FishingImpactCache
    mpa_hash = str(mpa.geometry_final.wkt.__hash__())
    if port is None:
        cache = FishingImpactCache.objects.filter(wkt_hash=mpa_hash, group=group)
    else:
        cache = FishingImpactCache.objects.filter(wkt_hash=mpa_hash, group=group, port=port)
    if len(cache) > 0:
        return True
    return False

'''
Checks to see if cache is complete and current on a given group
Returns True if cache is both complete and current
Called by mpa_cache_is_available_and_current
'''          
def cache_is_complete_and_uptodate(mpa, cache, group, port=None):
    if cache_is_complete(cache, group, port) and cache_is_uptodate(mpa, cache):
        return True
    return False

'''
Checks to see if cache is up to date (allowed uses have not changed) on a given group
Returns True if cache is current
Called by cache_is_complete_and_uptodate
'''     
def cache_is_uptodate(mpa, cache):
    if len(cache) > 0 and uses_have_not_changed(mpa, cache):
        return True
    return False

'''
Checks to see if cache is complete (all maps for the given port (or all ports) are represented) on a given group
Returns True if cache is current
Called by cache_is_complete_and_uptodate
'''     
def cache_is_complete(cache, group, port=None):
    from utilities import num_maps
    if len(cache) == num_maps(group, port):
        return True
    return False
 
'''
Retrieves cached entries for this mpa, group, port
Returns one of the following:
    an mpa specific cache (if available)-- a cache retrieval specific to this mpa (matching id and geometry), 
    or if mpa specific is not available,
    an arbitrary mpa cache -- a cache retrieval that is related to this mpa (same geometry)
    or if no cache is available,
    an empty query set
Called from MpaAnalysis.get_mpa_results and print_mpa_report (both with single_port) and mpa_cache_is_available_and_current 
''' 
def get_mpa_cache(mpa, group, single_port=None):
    from utilities import ensure_proper_name
    group = ensure_proper_name(group)
    from nc_mlpa.econ_analysis.models import FishingImpactCache
    mpa_hash = str(mpa.geometry_final.wkt.__hash__())
    if single_port is None:
        cache = FishingImpactCache.objects.filter(wkt_hash=mpa_hash, group=group)
        mpa_specific_cache = FishingImpactCache.objects.filter(mpa__id=mpa.id, wkt_hash=mpa_hash, group=group)
    else:
        cache = FishingImpactCache.objects.filter(wkt_hash=mpa_hash, group=group, port=single_port)
        mpa_specific_cache = FishingImpactCache.objects.filter(mpa__id=mpa.id, wkt_hash=mpa_hash, group=group, port=single_port)
    if cache_is_complete_and_uptodate(mpa, mpa_specific_cache, group, single_port):
        return mpa_specific_cache
    elif len(cache) > 0:
        #get results related to an arbitrary single mpa
        if single_port is None:
            arbitrary_mpa_cache = FishingImpactCache.objects.filter(mpa__id=cache[0].mpa.id, wkt_hash=mpa_hash, group=group)
        else:
            arbitrary_mpa_cache = FishingImpactCache.objects.filter(mpa__id=cache[0].mpa.id, wkt_hash=mpa_hash, group=group, port=single_port)
        if cache_is_complete_and_uptodate(mpa, arbitrary_mpa_cache, group, single_port):
            return arbitrary_mpa_cache
    return FishingImpactCache.objects.none() #empty QuerySet
                
'''
Checks to see if allowed uses for the mpa differ from those cached for this (or related) mpa
Called from cache_is_uptodate 
'''   
def uses_have_not_changed(mpa, single_mpa_cache):
    from nc_mlpa.econ_analysis.models import FishingImpactCacheAllowedUse
    mpa_uses = mpa.allowed_uses.all()
    cache_uses = FishingImpactCacheAllowedUse.objects.filter(mpaid=single_mpa_cache[0].mpa.id)
    old_uses = [cache.use for cache in cache_uses]
    if uses_differ(old_uses, mpa_uses):
        return False
    return True
    
'''
Checks to see if two lists of allowed uses differ 
Called from uses_have_not_changed
'''   
def uses_differ(old_uses, new_uses):
    if len(old_uses) != len(new_uses):
        return True
    for use in old_uses:
        if use not in new_uses:
            return True
    return False
    